﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BaseTool
{
    /// <summary>
    /// 日志类
    /// </summary>
    public class Log
    {
        /// <summary> 向其他控件发送消息 </summary>
        public static Action<LogMessage> LogAction;

        /// <summary> 日志消息队列 </summary>
        private static ConcurrentQueue<LogMessage> queue = null;
        /// <summary> 消息队列线程 </summary>
        private static Thread threadWriteLog = null;
        /// <summary> 创建线程锁 </summary>
        private static object threadLock = new object();
        /// <summary> 写入文件锁，防止文件冲突 </summary>
        private static object logLock = new object();

        #region 外部调用
        /// <summary>
        /// Log保存函数
        /// </summary>
        /// <param name="message">信息内容</param>
        /// <param name="logType">Log信息类型</param>
        /// <param name="isShow">是否显示</param>
        public static void SaveLog(string message, LogType logType = LogType.Run, bool isShow = true)
        {
            SaveLog(message, logType, isShow, Color.Black);
        }
        /// <summary>
        /// Log保存函数
        /// </summary>
        /// <param name="message">信息内容</param>
        /// <param name="color">显示颜色</param>
        /// <param name="logType">Log信息类型</param>
        public static void SaveLog(string message, LogType logType, Color color)
        {
            SaveLog(message, logType, true, color);
        }
        /// <summary>
        /// Log保存函数
        /// </summary>
        /// <param name="message">信息内容</param>
        /// <param name="logType">Log信息类型</param>
        /// <param name="isShow">是否显示</param>
        /// <param name="color">显示颜色</param>
        public static void SaveLog(string message, LogType logType, bool isShow, Color color)
        {
            try
            {
                DateTime now = DateTime.Now;

                switch (logType)
                {
                    case LogType.Run:
                    case LogType.Sensor:
                        {
                            //运行日志
                            LogMessage logInfo = new LogMessage();
                            logInfo.time = now;
                            logInfo.logType = logType;
                            logInfo.message = message;
                            logInfo.fileName = $"{PathTool.DataLogPath}Run\\{now.ToString("yyyy_MM_dd")}.log";
                            logInfo.isShow = isShow;
                            logInfo.color = color;
                            //将日志放入并行队列中
                            SaveLog(logInfo);
                            break;
                        }
                    default:
                        {
                            LogMessage logInfo = new LogMessage();
                            logInfo.time = now;
                            logInfo.logType = logType;
                            logInfo.message = $"日志代码类型设置错误，未识别类型[{logType}]，日志信息内容[{message}]";
                            logInfo.fileName = $"{PathTool.DataLogPath}Error\\{now.ToString("yyyy_MM_dd")}_Software.log";
                            logInfo.isShow = false;
                            logInfo.color = Color.Red;
                            //将日志放入并行队列中
                            SaveLog(logInfo);
                            break;
                        }
                }
            }
            catch (Exception ex)
            {
                SaveError(ex);
            }
        }
        /// <summary>
        /// Log保存函数
        /// </summary>
        /// <param name="ex">异常对象</param>
        /// <param name="errorMsg">额外报错信息</param>
        /// <param name="logType">额外报错类型</param>
        public static void SaveError(Exception ex, string errorMsg = null, LogType logType = LogType.Run)
        {
            try
            {
                DateTime dateTime = DateTime.Now;

                #region 构建异常信息
                StringBuilder sb = new StringBuilder();
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine($"----------     {dateTime.ToString()}     ----------");
                sb.AppendLine($"Message: {ex.Message}");
                sb.AppendLine($"Source: {ex.Source}");
                sb.AppendLine($"StackTrace: {ex.StackTrace}");
                sb.AppendLine($"TargetSite: {ex.TargetSite.ToString()}");
                sb.AppendLine($"ex: {ex.ToString()}");
                StackTrace stackTrace = new StackTrace(ex, true);
                StackFrame[] stackFrames = stackTrace.GetFrames();
                for (int i = 0; i < stackFrames.Length; i++)
                {
                    //过滤无关帧
                    if (stackFrames[i].GetMethod().DeclaringType.Namespace.StartsWith("System"))
                        continue;
                    //添加无关帧信息
                    sb.AppendLine($"[{i}] Method: {stackFrames[i].GetMethod().Name} - File: {stackFrames[i].GetFileName()} - Line: {stackFrames[i].GetFileLineNumber()}");
                }
                #endregion

                //详细报错
                LogMessage logError = new LogMessage();
                logError.time = dateTime;
                logError.logType = LogType.Error;
                logError.message = sb.ToString();
                logError.fileName = $"{PathTool.DataLogPath}Error\\{logError.GetTimeString1()}.log";
                logError.isShow = false;
                logError.color = Color.Red;
                SaveLog(logError);

                //简要报错
                LogMessage logInfo = new LogMessage();
                logInfo.time = dateTime;
                logInfo.logType = LogType.Error;
                logInfo.message = $"Error：{ex.Message}";
                logInfo.fileName = $"{PathTool.DataLogPath}Error\\{logInfo.GetTimeString1()}_Info.log";
                logInfo.isShow = true;
                logInfo.color = Color.Red;
                SaveLog(logInfo);

                //额外报错
                if (!string.IsNullOrWhiteSpace(errorMsg))
                    SaveLog(errorMsg, logType, true, Color.Red);
            }
            catch (Exception ex2)
            {
                Debug.WriteLine($"请检查Log.SaveError()，此处存在致命报错！{ex2.Message}");
            }
        }
        #endregion

        #region 内部处理
        /// <summary>
        /// 初始化线程并添加日志并发
        /// </summary>
        private static void SaveLog(LogMessage logMessage)
        {
            #region 初始化
            //日志消息队列初始化
            if (queue == null)
            {
                lock (threadLock)
                {
                    if (queue == null)
                        queue = new ConcurrentQueue<LogMessage>();
                }
            }
            //循环线程初始化
            if (threadWriteLog == null)
            {
                lock (threadLock)
                {
                    if (threadWriteLog == null)
                    {
                        threadWriteLog = new Thread(ThreadQueue);
                        threadWriteLog.IsBackground = true;
                        threadWriteLog.Start();
                    }
                }
            }
             
            #endregion
            //添加并发日志
            queue.Enqueue(logMessage);
        }
        /// <summary>
        /// 后台线程处理
        /// </summary>
        private static void ThreadQueue()
        {
            while (!Machine.WillExit)
            {
                try
                {
                    Thread.Sleep(1);

                    //不包含信息则重复循环
                    if (!queue.Any())
                        continue;

                    //尝试取出信息
                    LogMessage tempMsg = null;
                    if (!queue.TryDequeue(out tempMsg))
                        continue;

                    //时间+信息
                    string timeMsg = tempMsg.GetTimeMessage();

                    //1 写入日志
                    SaveLog(tempMsg.fileName, timeMsg);

                    //2 向其他控件发送消息
                    LogAction?.Invoke(tempMsg);
                }
                catch (Exception ex)
                {
                    SaveError(ex);
                }
            }
        }
        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="fullName"></param>
        /// <param name="msg"></param>
        private static void SaveLog(string fullName, string msg)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(fullName))
                    return;

                lock (logLock)
                {
                    FileInfo log = new FileInfo(fullName);
                    if (!log.Directory.Exists)
                        log.Directory.Create();
                    using (FileStream fs = new FileStream(fullName, FileMode.Append))
                    {
                        using (StreamWriter sw = new StreamWriter(fs))
                        {
                            sw.WriteLine(msg.TrimEnd());
                            sw.Flush();
                            sw.Close();
                            fs.Close();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                SaveError(ex, $"无法写入报错信息：{msg}", LogType.Error);
            }
        }
        #endregion
    }

    /// <summary>
    /// 传入的信息类
    /// </summary>
    public class LogMessage
    {
        /// <summary> 当前时间 </summary>
        public DateTime time = DateTime.Now;
        /// <summary> 信息类型 </summary>
        public LogType logType = LogType.Run;
        /// <summary> 信息内容 </summary>
        public string message = string.Empty;
        /// <summary> 文件名（完整路径） </summary>
        public string fileName = string.Empty;
        /// <summary> 是否显示到控件上 </summary>
        public bool isShow = true;
        /// <summary> 显示颜色 </summary>
        public Color color = Color.Black;

        /// <summary>
        /// 默认信息构造
        /// </summary>
        public LogMessage() { }

        /// <summary>
        /// 获取时间+信息文本
        /// </summary>
        /// <returns></returns>
        public string GetTimeMessage()
        {
            return time.ToString("yyyy/MM/dd HH:mm:ss    ") + message + Environment.NewLine;
        }
        /// <summary>
        /// 返回时间字符串，2025-2-25
        /// </summary>
        /// <returns></returns>
        public string GetTimeString1()
        {
            return time.ToShortDateString().Replace("/", "-");
        }
        /// <summary>
        /// 返回时间字符串，2025/2/25
        /// </summary>
        /// <returns></returns>
        public string GetTimeString2()
        {
            return time.ToShortDateString();
        }
        /// <summary>
        /// 返回时间字符串，00:00:00
        /// </summary>
        /// <returns></returns>
        public string GetTimeString3()
        {
            return time.ToString("HH:mm:ss");
        }
    }
    /// <summary>
    /// Log信息类型
    /// </summary>
    public enum LogType
    {
        /// <summary> 运行 </summary>
        Run,
        /// <summary> 传感器 </summary>
        Sensor,
        /// <summary> Exception报错 </summary>
        Error
    }
}
